/* All scrmgr password checking - currently only tested for Sun */

/* compile generator program with:
   cc -DSM_SUNOS -DMAIN -I/usr/local/include smpasswd.c -O -s -o smpasswd

   ... or in TurboC++ 1.0 :

tcc -ml -g0 -G -N- -DSM_TURBOC -DMAIN -c -I/tc/include smpasswd.c 
tcc -ml -g0 -G -N- -L/tc/lib -osmpasswd.exe smpasswd.obj mylibl.lib
del smpasswd.obj

*/

/* Password file:
   ~/.smpasswd or
   /etc/smpasswd

or, under MSDOS: 

   ./smpasswd.pwd or 
   /etc/smpasswd.pwd

   Contents:
   	# comments
   	Expire Time (long seconds)
	Password
	[Demo]

    Check:
    	Time stamp on password file <= current time
	Time stamp on input file <= current time (sm_check_timestamp)
	Password hashes correctly
	Expire time > current time 
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <getopt.h>
#define SM_CLASS extern
#include "smlib.h"
#if HAS_UNAME
#include <sys/utsname.h>
#include <unistd.h>
#endif

static char fail_msg[] = 
    "Password expired - contact your supplier for new password";

static char no_file_msg[] = 
#if NEEDS_PATH_FOR_PROGS
    "Can't find smpasswd.pwd";
#else
    "Can't find ~/.smpasswd or /etc/smpasswd";
#endif

static struct {
    int		is_demo;
    int		days_left;
} global;

static void passwd_abort ARG2(CHAR *, msg, int, code)
{
    fprintf(stderr, "%s: code %d\n", msg, code);
    exit(1);
}

int sm_check_timestamp ARG1(CHAR *, filename)
{
    struct stat	statbuf;
    time_t	now;

    if (stat(filename, &statbuf) == 0) {

	now = time(NULL);

	if (statbuf.st_mtime > now) {
	    fprintf(stderr, "%s: code %d\n", fail_msg, 10);
	    return(1);
	}
    }
    return(0);
}

static void trim ARG1(char*, buf)
{
    char	*p;

    if ((p = strchr(buf, '\n')) != NULL)
	*p = 0;
    if ((p = strchr(buf, ' ')) != NULL)
	*p = 0;
    if ((p = strchr(buf, '\t')) != NULL)
	*p = 0;
}

#define PASSWD_LEN 12
/* PASSWD_LEN characters from A-Z */
void static create_password ARG3(CHAR *, 	check_password, 
				 CHAR *,	expire_time, 
				 CHAR *,	hostname)
{
    int		key = 0, i;
    char	*c, *d;

    trim(check_password);
    trim(expire_time);
    trim(hostname);

    for (i = 0; i < PASSWD_LEN; i++) {
	for (c = expire_time; *c; c++)
	    for (d = hostname; *d; d++)
		key = 17 * key + 31 * toupper(*c) + 41 * toupper(*d);
	check_password[i] = 'A' + abs(key % 26);
    }
    check_password[PASSWD_LEN] = 0;
    trim(check_password);
}

void sm_warn_passwd()
{
    static char *rudeness[] = {
"Thanks for trying this program! Use beyond the trial period requires",
"licence purchase and registration. Licencing is not optional.",
"This is not free software.",
"",
"To purchase this software (and get rid of this message), contact:",
"",
"         Compilations Ltd.",
"         Fx: +852-849-4801",
NULL
};

    char	**c = rudeness;
    
    while (global.is_demo && c && *c) {
	fputs(*c++, stderr);
	fputc('\n', stderr);
    }

#ifndef INSECURE
    if (global.days_left < 14)
	fprintf(stderr, "The password for Screen Manager will expire soon.\n");
#endif
}

int sm_check_passwd()
{
#ifndef INSECURE
#define LINESIZE 100
    char	filename[LINESIZE];
    char	*p;
    int		file = -1;
    FILE	*f;
    struct stat	statbuf;
    time_t	now;
    char	expire_time_buf[LINESIZE];
    time_t	expire_time;
    char	password[LINESIZE];
    char	check_password[LINESIZE];
    char	hostname_buf[LINESIZE];
#if NEEDS_PATH_FOR_PROGS
    extern char *sm_dirname;

    sprintf(filename, "%ssmpasswd.pwd", sm_dirname);
    file = open(filename, O_RDONLY);
#else 
    extern long	gethostid();

    p = getenv("HOME");
    if (p && *p) {
	strcpy(filename, p);
	if (filename[strlen(filename) - 1] != '/')
	    strcat(filename, "/");
	strcat(filename, ".smpasswd");
	file = open(filename, O_RDONLY);
    }

    if (file == -1) {
	strcpy(filename, "/etc/smpasswd");
	file = open(filename, O_RDONLY);
    }

#endif
    if (file == -1) 
	passwd_abort(no_file_msg, 1);

    if (fstat(file, &statbuf))
	passwd_abort("Can't stat password file", 2);

    close(file);

    file = open(filename, O_WRONLY);
    if (file == -1)
	passwd_abort("Can't open password file for writing", 3);
    close(file);

    f = fopen(filename, "r");
    if (f == NULL) 
	passwd_abort(no_file_msg, 4);

    now = time(NULL);

    if (statbuf.st_mtime > now)
	passwd_abort(fail_msg, 5);

    /* Read the password file in */
    do {
	expire_time_buf[0] = 0;
	if (fgets(expire_time_buf, LINESIZE, f) == NULL)
	    passwd_abort(fail_msg, 6);
    } while (expire_time_buf[0] == '#');
    do {
	password[0] = 0;
	if (fgets(password, LINESIZE, f) == NULL)
	    passwd_abort(fail_msg, 7);
    } while (password[0] == '#');
    sm_toupper(password);
    do {
	hostname_buf[0] = 0;
	if (fgets(hostname_buf, LINESIZE, f) == NULL)
	    break;
    } while (*hostname_buf == '#');
    sm_toupper(hostname_buf);
    trim(hostname_buf);
    if (strcmp(hostname_buf, "DEMO") == 0)
	global.is_demo = 1;
    else
	global.is_demo = 0;

#if HAS_UNAME
    if (hostname_buf[0] == 0) {
	struct utsname buf;

	uname(&buf);
	strcpy(hostname_buf, buf.nodename);
    }
#elif HAS_GETHOSTID
    if (hostname_buf[0] == 0)
	sprintf(hostname_buf, "%lx", gethostid());
#endif

    expire_time = atol(expire_time_buf);
    if (expire_time < now)
	passwd_abort(fail_msg, 8);

    trim(password);
    create_password(check_password, expire_time_buf, hostname_buf);
    if (strcmp(check_password, password))
	passwd_abort(fail_msg, 9);

    fclose(f);
    utimes(filename, NULL); /* set timestamp on password file */

    global.days_left = (expire_time - now) / (3600 * 24);
    return(0);
#else
    return(0);
#endif /* INSECURE */
}				   

#ifdef MAIN

CHAR *smpasswd_rcsid(void) {
    return("$Id: smpasswd.c,v 1.3 2008/02/19 13:15:50 bhepple Exp $");
}

static CHAR *usage_msg[] = {
"Creates SCRMGR passwords.",
"",
"hostid     : of target Sun.",
"YY/MM/DD   : Year, month & day to expire (12am).",
NULL
};

static void usage()
{
    CHAR **c;
    
    fprintf(stderr, 
	    "Usage: smpasswd hostid YY/MM/DD\n");
    c = usage_msg;
    while (*c) {
        fputs(*c, stderr);
        fputc('\n', stderr);
        c++;
    }
    exit(2);
}

void sm_toupper ARG1(CHAR *, p)
{
    while (*p) {
	if (islower(*p))
	    *p = toupper(*p);
	p++;
    }
}

static CHAR	*sm_progname, *sm_dirname, pname[9];

main(argc, argv)
    int		argc;
    char	**argv;
{
    char	passwd[LINESIZE];
    struct tm	tmbuf;
    time_t	expire_time;
    char	expire_time_buf[LINESIZE];
    int		c;

    fname(pname, argv[0]);
    sm_progname = pname;

#if NEEDS_PATH_FOR_PROGS
    {
	CHAR	*p;
#if BACKSLASH_AND_SLASH
	if ((p = strrchr(sm_progname, '\\')) != NULL)
	    *++p = 0;
	else
#endif 
	if ((p = strrchr(sm_progname, '/')) != NULL)
	    *++p = 0;
#if USE_DRIVE_LETTER
	else if ((p = strchr(sm_progname, ':')) != NULL)
	    *++p = 0;
#endif
	sm_dirname = sm_progname;
    }
#endif
    while ((c = getopt(argc, argv, "?")) != EOF)
        switch (c) {
        default:  
	    usage(); 
	    break;
        }
    
    if ((optind >= argc) || (strlen(argv[optind]) == 0))
	usage();

    strptime(argv[2], "%y/%m/%d", &tmbuf);
    expire_time = timegm(&tmbuf);
    sprintf(expire_time_buf, "%ld", expire_time);
    sm_toupper(argv[1]);
    create_password(passwd, expire_time_buf, argv[1]);
    puts(expire_time_buf);
    puts(passwd);
    puts(argv[1]);
    fflush(stdout);
    fprintf(stderr, "About %ld days\n", 
	    (expire_time - time(NULL)) / (3600 * 24));
    sm_check_passwd();
    fputs("Installed password is ok\n", stderr);
    sm_warn_passwd();
}
#endif /* MAIN */

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
